<?php
	/*
		GP_ActiveRecord::setDefaultDBConnection($GP_db); 
	
		$livre = new GP_ActiveRecord('livres', 1);
		echo $livre -> titre;
		
		$livre = new GP_ActiveRecord('livres');
		$livre -> titre = 'Livre 1';
		$livre -> soumettre(); //insertion 
		$livre -> titre = 'Livret 1';
		$livre -> soumettre(); //mise a jour 
	
		$logs = new GP_ActiveRecord('bo_logs');
		$select = array(
			'lasession',
			'action'
		);
		$lignes = $logs -> get($select);
		foreach ($lignes as $ligne)
			echo $ligne["lasession"] . ' => ' . $ligne["action"] . '<p />';
		$logs -> supprimer('id = 5');
	*/
	
	class GP_ActiveRecord 
	{
		protected static $_defaultDBConnection;
		protected $_champs = 			array();
		protected $_cle      = 			null;
		protected $_cleChamp = 			null;
		protected $_dbConnection = 		null;
		protected $_nomTable = 			null;
		protected $tab_alias = 			array();
		protected $total_tab = 			array();
		var $sql_requete;
		static $_instance;
		var $nb_resultats = 			0;
		
		public function __construct($tableName, $cle = null, $jointure = '', PDO $dbConnection = null)
		{
			if (!is_array($tableName))
				$this -> _nomTable = $tableName;
			else
				$this -> _nomTable = $jointure;
			$this -> _cle = $cle;
			$this -> _dbConnection = (!$dbConnection && self::$_defaultDBConnection) ? self::$_defaultDBConnection : $dbConnection;						
			$this -> _dbConnection -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		}
		
		function table_aliases($table)
		{
			if (is_array($table))
			{
				foreach ($table as $t)
					$this -> table_aliases($t);
				return;
			}
			
			if (strpos($table, ',') !== false)
				return $this -> table_aliases(explode(',', $table));
		
			if (strpos($table, " ") !== false)
			{
				$table = preg_replace('/ AS /i', ' ', $table);
				
				$table = trim(strrchr($table, " "));

				if (!in_array($table, $this -> tab_alias))
					$this -> tab_alias[] = $table;
			}
		}

		public function getKey()
		{
			return $this -> _cle;
		}
			
		public function __get($champ)
		{
			if (!is_null($this -> _cle))
				return $this -> getValue($champ);
		}

		public function getValue($champ)
		{
			if(!$this -> _champs)
				$this -> describe();
			
			if(isset($this -> _champs[$champ]))
			{
				if(!$this -> _champs[$champ]['value'])
					$this -> select();
				
				return $this -> _champs[$champ]['value'];
			} 
			else
				GP_debug::erreur('Champ inconnu `' . $champ . '`');
		}
		
		public function __set($champ, $valeur)
		{
			return $this -> setValue($champ, $valeur);
		}
		
		public function setValue($champ, $valeur)
		{
			if(!$this -> _champs)
				$this -> describe();
			
			if(isset($this -> _champs[$champ])){
				$this -> _champs[$champ]['value']   = $valeur;
				$this -> _champs[$champ]['changed'] = true;
				
				return $this -> _champs[$champ]['value'];
			}
			else
				GP_debug::erreur('Champ inconnu `' . $champ . '`');
		}		

		public function setDBConnection(PDO $db)
		{
			return $this -> _dbConnection = $db;
		}
		
		public function getDBConnection()
		{
			return $this -> _dbConnection;
		}

		public static function setDefaultDBConnection(PDO $db)
		{
			return self::$_defaultDBConnection = $db;
		}

		public static function getDefaultDBConnection()
		{
			return self::$_defaultDBConnection;
		}
		
		public function soumettre()
		{
			if(!$this -> _champs)
				$this -> describe();
			
			if(!$this -> _cle)
				$this -> _cle = $this -> insert();
			else
				$this -> update();

			$this -> changeFields();
		}
		
		protected function insert()
		{
			if(!$this -> _champs)
				$this -> describe();
			
			$champs = array();
			$valeurs = array();	
			
			foreach($this -> _champs as $champ => $data)
			{
				if($champ == $this -> _cleChamp && !$this -> _cle)
					continue;	
				
				$rawValue = ":{$champ}";
				$valeurs[":{$champ}"] = $this -> formatValue($data['value'], $data['fieldType']);
				
				$champs['gauche'] [$champ] = "`{$champ}`";
				$champs['droite'][$champ] = $rawValue;
			}
			
			$sql = "INSERT INTO `{$this -> _nomTable}` (" . implode(', ', $champs['gauche']) . ') VALUES (' . implode(', ', $champs['droite']) . ');';
		
			$db = $this -> getCheckedDBConnection();
			$stmt = $db -> prepare($sql);
			$rs = $stmt -> execute($valeurs);
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
					
				$erreur_info = $res -> errorInfo();
					
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
					
				GP_debug::erreur($erreur, 1);
			}
			
			return $this -> _cle = $db -> lastInsertId($this -> _cleChamp);
		}
		
		protected function changeFields()
		{
			foreach($this -> _champs as $champ => &$data)
				$data['changed'] = false;
		}

		protected function update()
		{
			if(!$this -> _champs)
				$this -> describe();
			
			if(!$this -> _cle || !$this -> _cleChamp)
				GP_debug::erreur("('{$this -> _cle}', `{$this -> _cleChamp}`) invalides");
			
			$set = array();
			$valeurs = array();	
			
			foreach($this -> _champs as $champ => $data)
			{
				if($champ == $this -> _cleChamp || !$data['changed'])
					continue;	
				
				$rawValue = ":{$champ}";
				$valeurs[":{$champ}"] = $this -> formatValue($data['value'], $data['fieldType']);
				
				$set[$champ] = "`{$champ}` = {$rawValue}";
				
			}
			
			$valeurs[':PrimaryKey'] = $this -> _cle;
			
			if(!$set)
				return;
			
			$sql = "UPDATE `{$this -> _nomTable}` SET " . implode(', ', $set) . " WHERE `{$this -> _cleChamp}` = :PrimaryKey";
		
			
			$stmt = $this -> getCheckedDBConnection() -> prepare($sql);
			$rs = $stmt -> execute($valeurs);
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
					
				$erreur_info = $res -> errorInfo();
					
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
					
				GP_debug::erreur($erreur, 1);
			}
		}
		
		public function get($champs = array(), $cond = '', $uppercase = false)
		{
			$db = $this -> getCheckedDBConnection();
			
			if (!$uppercase):
				if (!count($champs))
					$champs = '*';
				else
					$champs = implode(', ', $champs);
			else:
				if (!count($champs))
					$champs = 'UPPER(*)';
				elseif (count($champs) == 1)
					$champs = 'UPPER(' . $champs[0] . ')';
				else
					$champs = implode(', ', $champs);
			endif;
			
			if (!strlen($cond))
				$sql = "SELECT {$champs} FROM {$this -> _nomTable}";
			else
				$sql = "SELECT {$champs} FROM {$this -> _nomTable} WHERE " . $cond;

			$this -> sql_requete = $sql;
			$stmt = $db -> prepare($sql);
			$stmt -> setFetchMode(PDO::FETCH_ASSOC);
			$rs = $stmt -> execute();
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
					
				$erreur_info = $res -> errorInfo();
					
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
					
				GP_debug::erreur($erreur, 1);
			}
			
			$retour = $stmt -> fetchall();
			$this -> nb_resultats = count($retour);
			return $retour;
		}
		
		public function join($jointure, $champs = array(), $cond = '')
		{
			$db = $this -> getCheckedDBConnection();
			
			if (!count($champs))
				$champs = '*';
			else
				$champs = implode(', ', $champs);			
			
			if (!strlen($cond))
				$sql = "SELECT {$champs} FROM `{$this -> _nomTable}` {$jointure}";
			else
				$sql = "SELECT {$champs} FROM `{$this -> _nomTable}` {$jointure} WHERE " . $cond;
			
			$stmt = $db -> prepare($sql);
			$stmt -> setFetchMode(PDO::FETCH_ASSOC);
			$rs = $stmt -> execute();
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
					
				$erreur_info = $res -> errorInfo();
					
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
					
				GP_debug::erreur($erreur, 1);
			}
			
			$retour = $stmt -> fetchall();
			$this -> nb_resultats = count($retour);
			return $retour;
		}
		
		public function supprimer($cond = '')
		{
			$db = $this -> getCheckedDBConnection();
			
			if(strlen($this -> _cle) && !strlen($cond))
			{
				$this -> describe();
				$sql = "DELETE FROM `{$this -> _nomTable}` WHERE `{$this -> _cleChamp}` = :key";
				
				$stmt = $db -> prepare($sql);
				$stmt -> setFetchMode(PDO::FETCH_ASSOC);
				$rs = $stmt -> execute(
					array(
						':key' => $this -> _cle
					)
				);
			}
			
			else
			{
				if (!strlen($cond))
					$sql = "DELETE FROM `{$this -> _nomTable}`";
				else
					$sql = "DELETE FROM `{$this -> _nomTable}` WHERE " . $cond;
				
				$stmt = $db -> prepare($sql);
				$rs = $stmt -> execute();
			}
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
						
				$erreur_info = $res -> errorInfo();
						
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
						
				GP_debug::erreur($erreur, 1);
			}
		}
		
		protected function select()
		{
			if(!$this -> _champs)
				$this -> describe();
			
			if(!$this -> _cle || !$this -> _cleChamp)
				GP_debug::erreur("('{$this -> _cle}', `{$this -> _cleChamp}`) sont invalides");
			
			$db = $this -> getCheckedDBConnection();	
			$sql = "SELECT * FROM `{$this -> _nomTable}` WHERE `{$this -> _cleChamp}` = :key LIMIT 1";
			$stmt = $db -> prepare($sql);
			$stmt -> setFetchMode(PDO::FETCH_ASSOC);
			$rs = $stmt -> execute(
				array(
					':key' => $this -> _cle
				)
			);
			
			if($rs === false)
			{
				$erreur = $sql;
				$erreur .= '<p />';
					
				$erreur_info = $res -> errorInfo();
					
				$erreur .= 'Erreur ' . $erreur_info[1] . ' ' . sl($erreur_info[2]);
					
				GP_debug::erreur($erreur, 1);
			}
			
			foreach($stmt -> fetch() as $champ => $valeur)
			{
				assert(isset($this -> _champs[$champ]));
				$this -> _champs[$champ]['changed'] = false;
				$this -> _champs[$champ]['value'] = $valeur;
			}
			
		}
		
		protected function describe()
		{
			if(!$this -> _nomTable)
				GP_debug::erreur("Nom de table invalide");
			
			$db = $this -> getCheckedDBConnection();
			
			$sql = "DESCRIBE `{$this -> _nomTable}`;";
			$resultat = $db -> query($sql, PDO::FETCH_ASSOC);
			
			if(!$resultat)
			{
				$erreur = $db -> errorInfo();
				GP_debug::erreur("[$erreur[0]}: {$erreur[2]}", $erreur[1]);
			}
			
			$mysql_index = array(
				'',
				'PRI', 
				'MUL', 
				'UNI'
			);
			
			$retour = array();
			
			foreach($resultat -> fetchAll() as $row)
			{
				$champ = array();
				$champ['index'] = array_search($row['Key'], $mysql_index);
				
				
				assert(!($champ['index'] === 1 && $this -> _cleChamp));
				
				if($champ['index'] === 1)
					$this -> _cleChamp = $row['Field'];
				
				
				$champ['changed'] = false;
				$type = explode('(', $row['Type'], 2);

				$champ['fieldType'] = strtolower($type[0]);
				if(isset($type[1]))
					$champ['fieldLength'] = trim($type[1], ')');
				
				$retour[$row['Field']] = $champ; 
			}
							
			return $this -> _champs = $retour;
		}
		
		protected function getCheckedDBConnection()
		{
			$db = $this -> getDBConnection();
			if(!$db)
			{
				$defaultDB = $this -> getDefaultDBConnection();
				if(!$defaultDB)
					GP_debug::erreur('No database connection');
				
				$db = $this -> setDBConnection($defaultDB);
			}
			
			return $db;
		}
		
		protected function formatValue($valeur, $champType)
		{
			$mysql_dateformats = array(
				'datetime' => 'Y-m-d H:i:s', 
				'time' => 'H:i:s', 
				'date' => 'Y-m-d'
			);

			if(isset($mysql_dateformats[$champType]))
				$valeur = date($mysql_dateformats[$champType], is_numeric($valeur) ? $valeur : strtotime($valeur));			
				
			if($data['indexType'] == 'int')
				$valeur = (int)$valeur;
			
			return $valeur;
		}
		
	}
?>